Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

Swap available balance #1892

Merged
merged 20 commits into from
Oct 29, 2019
Merged

Swap available balance #1892

merged 20 commits into from
Oct 29, 2019

Conversation

Eknir
Copy link
Contributor

@Eknir Eknir commented Oct 18, 2019

This PR creates a new function AvailableBalance. This call is also exposed via the API
This call is needed for PR #1885

On top of this, the smart-contract function calls (Deposit and Withdraw) are now exposed from the go-bindings. These were needed to test the added functionality, but they will be useful later on as well. (Deposit is needed for #1886, for withdraw we don't have an issue yet).

closes #1884

Copy link
Contributor

@mortelli mortelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preliminary draft review ✌️

contracts/swap/swap.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Show resolved Hide resolved
swap/swap_test.go Outdated Show resolved Hide resolved
swap/swap_test.go Outdated Show resolved Hide resolved
swap/swap_test.go Outdated Show resolved Hide resolved
@@ -434,6 +451,19 @@ func (s *Swap) Balances() (map[enode.ID]int64, error) {
return balances, nil
}

// AvailableBalance returns the total balance of the chequebook against which new cheques can be written
func (s *Swap) AvailableBalance() (uint64, error) {
Copy link
Member

@ralph-pichler ralph-pichler Oct 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really recommend avoiding 2 blockchain wide event filters in one function, this can take quite a while on a real network (especially if using remote endpoints like infura). Instead we could compute this per peer using s.contract.availableBalanceFor(peer) + s.contract.paidOut(p.beneficiary) - p.getLastSentCheque().CumulativeAmount.

If we need this for all peers (not sure why we would though) we can compute liquidBalance() + SUM(s.contract.paidOut(peer)) - SUM(p.getLastSentCheque().CumulativeAmount). The last sum can be computed locally since we have all cheques in our store.

If we implement it that way we also don't need s.paidOut.

Copy link
Contributor Author

@Eknir Eknir Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Ralph, this call is needed for a node operator to figure out how much balance is still left in his chequebook against which new cheques can be written. It allows him to figure out what his spendable balance is and when he has to re-deposit. Furthermore, this call is needed by Swarm in order to verify that an to-be-send cheque is not overspending.

I will see how and if I can incorporate your proposed formula. Thanks for the suggestion!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Ralph,
Based on your comment, I removed paidOut from the swap structure and place availableBalance there instead. This change minimizes the need to ever call the ComputeAvailableBalance function to one (boot-up).

If we would go for implementing your (second) formula instead of how it is implemented now with the event filters, we need to do a call to the smart-contract to read paidOut for every peer.

Is this indeed quicker than the event filter? Note that Infura recently updated how they handle event queries: https://blog.infura.io/faster-logs-and-events-e43e2fa13773

If you think that what you propose is indeed much quicker, I will make the update!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: the option you presented and the one which is currently implemented are not entirely the same. They deviate in the case where a cheque is sent outside of Swarm and already cashed. The current implementation would recognize this (and calculate the available balance correctly from the point of cashing onwards), while your proposal would never recognize this cheque as it is not cashed by a peer, hence the availableBalance will always be off.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not consider the case with cheques outside of Swarm, this is always a user error and if done other things will break anyway. If we want the chequebook to be able to be used for other purposes we should introduce an RPC call for that so that the actual cheque creation and sending still takes place in the Swarm node.

Blockchain-wide event queries (from block 0 to "latest") can be really slow in my experience (Have you tested this call against ropsten or mainnet?). The Infura optimisation doesn't help much if it is then still slow for other users. (Since it's only being called once now it's not as big of a problem anymore).

While true that my formula does require more calls to paidOut(), this is an eth_call operation against the latest state which is a very fast operation. My per-peer formula would also have the advantage that it correctly accounts for any set hard deposit.

The current approach also doesn't update availableBalance correctly if the balance increases due to a cashed cheque after the node has started.

Copy link
Member

@ralph-pichler ralph-pichler Oct 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great answer Ralph. I am almost convinced :). Are you proposing to always do the call to availableBalance (not only during start-up), meaning we loop over the peers and do the required eth_call?

I would compute availableBalance on a per-peer basis whenever we want to send a cheque. Then it only requires two eth_call for every cheque sent.

If we want this availableBalance for all peers we have to do an eth_call for every peer but that would only happen when the RPC call is made and not during regular operation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great discussion guys!

i don't understand the issue as in-depth as you two, but at a high level, it seems sensible to me to query the source of the data (in this case the blockchain) for the latest info when it's needed.

but this, of course, is contingent upon these queries not slowing down the system significantly. it might be hard to verify this.

is the alternative (proposed by @Eknir) slower overall? or is it just less robust/more complicated? i understand the query from the genesis block to the present is slow according to @ralph-pichler, but it would be less queries in the long run, right?

Copy link
Contributor

@mortelli mortelli Oct 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need availableBalance for all peers in order to know how far away we are from needing to make a new deposit, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mortelli , it's an interesting discussion indeed. At this stage, I would personally favor robustness over efficiency, as we can always make efficiency gains later on, but robustness flaws are probably less nice to detect in a live network.
I think the alternative which I proposed was not necessarily slower, as it would only call to the blockchain upon boot-up. Subsequently, the idea was to keep a shadow view of the state of the blockchain, which requires little resources, but it is definitely less robust than calling the blockchain whenever needed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I may put my thoughts here. I pretty much have to agree with @ralph-pichler.

Blockchain-wide event queries (from block 0 to "latest") can be really slow in my experience (Have you tested this call against ropsten or mainnet?). The Infura optimisation doesn't help much if it is then still slow for other users. (Since it's only being called once now it's not as big of a problem anymore).

As @ralph-pichler says these events are horribly slow. We've been using them in Giveth and at one point it took 1.5hs to reprocess everything (around 5000 events). And this is after optimizations like querying since the contract deployment etc. Sure here it will not be that extreme in most of the cases but we should at minimum test it. And yeah chain reorgs or connection issues created a lot of errors and had to be handled.

We should not consider the case with cheques outside of Swarm, this is always a user error and if done other things will break anyway.

I also agree here. At least for the immediate future, I don't think we should be too bothered by this case. We should make an issue about this but not actively solve it now.

One of the usecases I can imagine is when user runs multiple swarm nodes with one chequebook. Still this brings a lot of problems to solve on the accounting side because it introduces all sorts of concurrency issues.

Copy link
Contributor

@mortelli mortelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good progress; some comments down below and some in the threads of the previous review.

i will review this again once you sort out the discussion with @ralph-pichler

swap/swap.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
swap/swap_test.go Show resolved Hide resolved
swap/swap_test.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Show resolved Hide resolved
swap/swap_test.go Outdated Show resolved Hide resolved
@Eknir Eknir mentioned this pull request Oct 22, 2019
swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Show resolved Hide resolved
@mortelli
Copy link
Contributor

thanks for addressing my comments @Eknir.

once you promote this from Draft I will be happy to re-review the whole thing.

@Eknir Eknir marked this pull request as ready for review October 24, 2019 06:44
contracts/swap/swap.go Outdated Show resolved Hide resolved
Copy link
Member

@ralph-pichler ralph-pichler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Please address @mortelli s comment before merging.

Just a sidenote: If we start summing cumulative payouts like in AvailableBalance we really should move to big.Int instead of uint64 soon.

continue
}
sentChequesWorth += ch.ChequeParams.CumulativePayout
paidOut, err := s.contract.PaidOut(nil, ch.ChequeParams.Beneficiary)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be changed in this PR but we really need to start thinking about contexts and timeouts when doing blockchain interaction. Here we call PaidOut in a loop, each call might take a while or never terminate at all. Can you open an issue for this in case there is none?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will

Copy link
Contributor Author

@Eknir Eknir Oct 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please comment on the issue @ralph-pichler , with your recommendation for setting context. Issue: #1910

Copy link
Contributor

@mortelli mortelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please rebase/merge with master

@@ -131,6 +164,10 @@ func (s simpleContract) CashChequeBeneficiary(opts *bind.TransactOpts, beneficia
return result, receipt, nil
}

func (s simpleContract) LiquidBalance(opts *bind.CallOpts) (*big.Int, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment exported func

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@Eknir Eknir merged commit 831a76e into master Oct 29, 2019
@Eknir Eknir deleted the swap-available-balance branch October 29, 2019 09:18
@acud acud added this to the 0.5.3 milestone Nov 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Available balance
6 participants